home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
pcr
/
pcr4_4.lha
/
DIST
/
interp
/
Interp.c
next >
Wrap
C/C++ Source or Header
|
1991-07-25
|
44KB
|
1,767 lines
/* begincopyright
Copyright (c) 1988 Xerox Corporation. All rights reserved.
Use and copying of this software and preparation of derivative works based
upon this software are permitted. Any distribution of this software or
derivative works must comply with all applicable United States export
control laws. This software is made available AS IS, and Xerox Corporation
makes no warranty about the software, its performance or its conformity to
any specification. Any person obtaining a copy of this software is requested
to send their name and post office or electronic mail address to:
PCR Coordinator
Xerox PARC
3333 Coyote Hill Rd.
Palo Alto, CA 94304
endcopyright */
/*
* Interp.c
*
* Interim (!) interpreter tool for PCR.
*
* Demers, October 3, 1990 2:17:08 pm PDT
*
*/
/*
This is a simple interpreter to be run in PCR. Run it by:
pcr: unixload /the/pathname/of/Interp.o
This registers a single comand,
pcr: eval <expression>
where the <expression> is a prefix expression with syntax described below.
The effect is to evaluate the expression an store its value in an interpreter
variable, which can later be examined and used as a subexpression in another
interpreter call. Syntax:
<expression> ::= <number>
Evaluates the number.
Legal numeric constants include 17, 010, 0x5c, but NOT -3
<expression> ::= <string>
Evaluates the string.
Legal string constants include "foo", "bar", but NOT "hello\n".
<expression> ::= <char>
Evaluates the character.
Legal char constants include 'x', '0', but NOT `\n`.
<expression> ::= @ <expression1> <expression2>
Evaluates the expressions, prints the value of <expression2> according
to the type of <expression1>.
Example: eval @ "" 0x126a4c might be used to pring a string.
<expression> ::= <binop> <expression1> <expression2>
Evaluates the <expression>s, combines them according to <op>.
Recognized <op>s are +, -, *, /.
<expression> ::= ^ <expression>
Evaluates the <expression>, interprets it as an address and returns
the word value stored there.
<expression> ::= < <variable> <expression>
Assign the value of <expression> to the specified interpreter <variable>
<expression> ::= [ <expression0> <expression1> ... <expressionk> ]
Function call: <expression0> should evaluate to the address of a function;
that function is called in a separate thread with the values of
<expression1> ... <expressionk> as arguments. The call times out after
awhile; the timeout interval (in msec) is controlled by the global variable
interpCallTimeout, which can be changed using the interpreter ...
<expression> ::= [! <expression0> <expression1> ... <expressionk> ]
[! is just like [ except the call is done in the interpreter's thread,
and can never time out.
<expression> ::= <variable>
An interpreter variable, which may be assigned to or evaluated.
Undefined variables are initially 0.
<expression> := <symbol>
The value of an external symbol in the loadstate. Numerous grotty
heuristics are applied during symbol lookup, such as appending a leading
underscore for C externals. For example,
pcr: eval XR_Msg
&11: 0x1a4f0 ...
The loadstate keeps internal text symbols, but not internal data or bss
symbols.
<expression> ::= <symbol>.<symbol>
The first symbol is expected to be a file or module name, the second
to be a text in that module. As above, heuristics are applied to
the file/module name (appending ".o") and the text symbol (prepending
an underscore, and some really arcane transformations related to the
procedure names generated by our Cedar/Mesa to C compiler). Example:
pcr: eval Interp.XR_GetSaveForInterp
&12: 0x60bf08 ...
An expression like this usually appears in the function position of a
call.
<variable> ::= &<number>
The result of each eval call is stored in a numbered variable, which
is accessible in later calls.
<variable> ::= &<symbol>
Variables can be named as well as numbered. Example:
eval + < &foo 17 &foo
&13: 34 ...
Note that foo is assigned to before it's evaluated ...
<variable> ::= &
& is the name of the most-recently-created numeric interpreter variable.
There is also a simple programmer's interface to this, through which you
can save values in interpreter variables and print messages on the interpreter's
output stream. These procedures appear at the end of the file.
Hint: if you don't know what a "Rope" is, avoid the procedures that deal with
them!
*/
#include "xr/BasicTypes.h"
#include "xr/Threads.h"
#include "xr/ThreadsBackdoor.h"
#include "xr/ThreadsMsgPrivate.h"
#include "xr/CommandLine.h"
#include "xr/CommandLoop.h"
#include "xr/UIO.h"
#include "xr/Errno.h"
#include "xr/IncrementalLoad.h"
extern char *( strchr(/* char *str, char c */) );
extern char *( strrchr(/* char *str, char c */) );
/* poor replacement for strtol library routine ... */
static int
DigitVal(c)
char c;
{
if( (c >= '0') && (c <= '9') ) return (c - '0');
if( (c >= 'a') && (c <= 'f') ) return (c - 'a' + 10);
if( (c >= 'A') && (c <= 'F') ) return (c - 'A' + 10);
return 999999;
}
int strtol(s, ptr, base)
char *s;
char **ptr;
int base;
{
int ans = 0;
char c, cLimit;
int sign = 1;
int digitVal;
if( s[0] == '+' ) s++;
if( s[0] == '-' ) { sign = -1; s++; }
if( base == 0 ) {
base = 10;
if( s[0] == '0' ) {
base = 8; s++;
if( s[0] == 'x' ) {
base = 16; s++;
}
}
}
for(;;) {
digitVal = DigitVal(*s);
if( digitVal >= base ) break;
ans = (ans * base) + digitVal;
s++;
}
if( ptr != NIL )
*ptr = s;
return ans * sign;
}
/*
* safe memory access ...
*/
static bool
ValidAddress(addr, eltCnt, eltBytes)
XR_Pointer addr;
unsigned eltCnt;
unsigned eltBytes;
{
XR_Pointer limit, stackLimit;
if( (addr & (eltBytes-1)) != 0 ) return FALSE;
if( addr < XR_GetPageSize() ) return FALSE;
limit = XR_ComputeAddress(
addr,
eltCnt*eltBytes,
XR_DONT_ROUND );
stackLimit =
XR_sysArea->sa_threadPool[XR_maxThreads-1].t_stack.stack_physLimit;
if( limit > stackLimit ) return FALSE;
return TRUE;
}
/*
* The Interpreter
*/
#define MAX_TOKENS 50
#define MAX_CALLARGS 20
#define MSG_BUFSIZE 512
unsigned defaultCallTimeoutMsec = 20000;
#define TYPE_ERR 0
#define TYPE_NONE 1
#define TYPE_CHAR 2
#define TYPE_STRING 3
#define TYPE_WORD32 4
#define TYPE_ADDRESS 5
#define TYPE_UNDEFSYM 6
#define TOK_ERR 0
#define TOK_EOF 1
#define TOK_ASSIGN 2
#define TOK_LBRACKET 3
#define TOK_LBRACKET2 4
#define TOK_RBRACKET 5
#define TOK_CAST 6
#define TOK_ADD 7
#define TOK_SUB 8
#define TOK_MUL 9
#define TOK_DIV 10
#define TOK_DEREF 11
#define TOK_SEPARATOR 12
#define TOK_VAR 13
#define TOK_CONST 14
typedef struct ReservedWordRep {
char *rw_string;
int rw_kind;
} * ReservedWord;
static struct ReservedWordRep reservedWords[] = {
{ "<", TOK_ASSIGN },
{ "[", TOK_LBRACKET },
{ "[!", TOK_LBRACKET2 },
{ "]", TOK_RBRACKET },
{ "@", TOK_CAST },
{ "+", TOK_ADD },
{ "-", TOK_SUB },
{ "*", TOK_MUL },
{ "/", TOK_DIV },
{ "^", TOK_DEREF },
{ ",", TOK_SEPARATOR },
{ ";", TOK_SEPARATOR }
};
typedef struct ExpRep {
struct ExpRep *exp_next;
char *exp_name;
int exp_type;
unsigned exp_value;
} * Exp;
typedef struct TokRep {
int tok_kind;
Exp tok_exp;
} * Tok;
typedef unsigned (*CProc)();
typedef struct CallRecordRep {
struct CallRecordRep *cr_next;
struct XR_MLRep cr_ml;
struct InterpDataRep *cr_idata;
CProc cr_calleeCProc;
struct XR_MesaProcRep cr_calleeMProc;
struct XR_CTRep cr_calleeThread;
struct XR_CVRep cr_cvDone;
Exp cr_result;
int cr_nArgs;
unsigned cr_args[MAX_CALLARGS];
} * CallRecord;
CallRecord callRecords = NIL;
static struct XR_MLRep callRecordsLock;
typedef struct InterpDataRep {
struct XR_MLRep idata_ml;
int idata_errPos;
char *idata_errMsg;
int idata_varIndex;
Exp idata_vars;
CProc idata_putProc /*(char *s, XR_Pointer clientData)*/;
XR_Pointer idata_putClientData;
unsigned idata_callTimeoutMsec;
struct TokRep idata_tokens[MAX_TOKENS];
char idata_msgBuf[MSG_BUFSIZE];
} * InterpData;
InterpData interpData = NIL; /* default interp handle */
InterpData interpDataForConstEval = NIL; /* for const evaluation */
/*
* message printing
*/
static void
Put4(idata, fmt, x1, x2, x3, x4)
InterpData idata;
char *fmt;
unsigned x1, x2, x3, x4;
/*
Assumes idata->idata_ml is held or idata is NIL.
*/
{
if( idata == NIL ) {
XR_ConsoleMsg(fmt, x1, x2, x3, x4); /* ??? */
return;
}
XR_SPrintF( idata->idata_msgBuf, fmt, x1, x2, x3, x4 );
(void)( (*(idata->idata_putProc))(
idata->idata_msgBuf, idata->idata_putClientData ));
}
#define PUT(idata) (void)( (*((idata)->idata_putProc))( \
(idata)->idata_msgBuf, (idata)->idata_putClientData ))
#define MSG0(idata,fmt) \
{ Put4((idata), (fmt), 0, 0, 0, 0); }
#define MSG1(idata,fmt,x1) \
{ Put4((idata), (fmt), (x1), 0, 0, 0); }
#define MSG2(idata,fmt,x1,x2) \
{ Put4((idata), (fmt), (x1), (x2), 0, 0); }
#define MSG3(idata,fmt,x1,x2,x3) \
{ Put4((idata), (fmt), (x1), (x2), (x3), 0); }
#define MSG4(idata,fmt,x1,x2,x3,x4) \
{ Put4((idata), (fmt), (x1), (x2), (x3), (x4)); }
/*
* error reporting
*/
static void
SetInterpErr(idata, pos, msg)
InterpData idata;
int pos;
char *msg;
{
if( (msg == NIL) || (idata->idata_errMsg == NIL) ) {
idata->idata_errMsg = msg;
idata->idata_errPos = pos;
}
}
#define INTERP_ERR(idata, pos, msg) { \
SetInterpErr((idata), (pos), (msg)); \
return (-1); \
}
#define CLEAR_INTERP_ERR(idata) \
SetInterpErr((idata), 0, NIL)
static int
ReservedWordKind(w)
char *w;
{
int i;
int ans;
for(
i = 0
; i < ((sizeof reservedWords)/(sizeof(struct ReservedWordRep)))
; i++
) {
if( strcmp(reservedWords[i].rw_string, w) == 0 )
return(reservedWords[i].rw_kind);
}
return 0;
}
static Exp
NewExp(idata, t, v)
InterpData idata;
int t;
unsigned v;
{
Exp e = (Exp)XR_calloc( sizeof(struct ExpRep), 1 );
e->exp_type = t;
e->exp_value = v;
return e;
}
static Exp
NewVar(idata, name)
InterpData idata;
char *name;
{
Exp e = (Exp)XR_calloc( sizeof(struct ExpRep), 1 );
e->exp_type = TYPE_NONE;
e->exp_value = 0;
if( name != NIL ) {
e->exp_name = (char *)XR_malloc( 1 + strlen(name) );
strcpy( e->exp_name, name );
}
return e;
}
static Exp
ScanVar(idata, name)
InterpData idata;
char *name;
{
Exp p, prev;
char tempNameBuf[20];
if( strcmp("&", name) == 0 ) {
XR_SPrintF(tempNameBuf, "&%d", idata->idata_varIndex );
name = tempNameBuf;
}
prev = NIL;
p = idata->idata_vars;
for(;;) {
if( p == NIL ) break;
if( strcmp(p->exp_name, name) == 0 ) break;
prev = p;
p = p->exp_next;
}
if( p != NIL ) {
if( prev == NIL ) {
idata->idata_vars = p->exp_next;
} else {
prev->exp_next = p->exp_next;
}
} else {
p = NewVar(idata, name);
}
p->exp_next = idata->idata_vars;
idata->idata_vars = p;
return p;
}
#define XXX 1
#undef XXX
int
InterpNameLookupC(name, valp)
char *name;
unsigned *valp;
{
XR_ILSymEntry ilse;
char *altText;
ilse = XR_ILGetMatchingSymEntryByName(
NIL, name, TRUE, WANT_ALL_TYPES, IGNORE_INTERNAL, 0 );
if( ilse == NIL ) {
ilse = XR_ILGetMatchingSymEntryByName(
NIL, name, TRUE, WANT_ALL_TYPES, IGNORE_NONE, 0 );
}
if( (ilse == NIL) && (name[0] != '_') ) {
altText = (char *)XR_malloc(2+strlen(name));
altText[0] = '_'; (void)strcpy(&(altText[1]), name);
ilse = XR_ILGetMatchingSymEntryByName(
NIL, altText, TRUE, WANT_ALL_TYPES, IGNORE_INTERNAL, 0 );
if( ilse == NIL ) {
ilse = XR_ILGetMatchingSymEntryByName(
NIL, altText, TRUE, WANT_ALL_TYPES, IGNORE_NONE, 0 );
}
}
if( ilse == NIL ) {
return (-1);
}
*valp = ((unsigned)(ilse->ilse_value));
return 0;
}
int
InterpNameLookupCedar(name, valp)
char *name;
unsigned *valp;
{
static unsigned *((*procFromNamedInterface)()) = NIL;
int ans;
char *tailP, *dotP;
char *interfaceName;
char *procName;
unsigned *cedarProcValue;
if( procFromNamedInterface == NIL ) {
ans = InterpNameLookupC( "_XR_ProcFromNamedInterface",
&procFromNamedInterface );
if( ans < 0 ) return ans;
}
if( (dotP = strchr(name, '.')) == NIL ) {
return(-1);
}
if( (tailP = strrchr(name, '.')) != dotP ) {
return(-1);
}
interfaceName = (char *)XR_malloc(dotP-name+1);
procName = (char *)XR_malloc(name+strlen(name)+1-tailP);
bcopy(name, interfaceName, dotP-name);
interfaceName[dotP-name] = 0;
strcpy(procName, tailP+1);
cedarProcValue = (*procFromNamedInterface)(interfaceName, procName, NIL, 0);
if( cedarProcValue == NIL ) {
return(-1);
}
(*valp) = (*cedarProcValue);
return 0;
}
static bool
MimosaFileNameMatch(specified, found, caseSensitive)
char *specified;
char *found;
bool caseSensitive;
{
char c;
char *p;
int specifiedLen;
int (*cmpFunc)();
extern int strncmp();
extern int strncasecmp();
specifiedLen = strlen(specified);
cmpFunc = ( caseSensitive ? strncmp : strncasecmp );
if( (*cmpFunc)(specified, found, specifiedLen) != 0 ) return FALSE;
p = found+specifiedLen;
if( (*cmpFunc)(p, ".c2c.o", 6) == 0 ) {
p += 6;
} else if( (*cmpFunc)(p, ".o", 2) == 0 ) {
p += 2;
}
if( *p == 0 ) return TRUE;
if( *p++ != '.' ) return FALSE;
if( *p++ != '~' ) return FALSE;
for(;;) {
c = *p++;
if( c == '~' ) break;
if( (c < '0') || (c > '9') ) return FALSE;
}
return TRUE;
}
static bool
MimosaSymMatch(sym, msym)
char *sym; /* symbol, pre-Mimosa/c2c */
char *msym; /* symbol, possibly transformed by Mimosa/c2c */
{
char c;
if( (*msym == '_') && (*sym != '_') ) msym++;
while( *sym ) {
if( *sym++ != *msym++ ) return FALSE;
}
if( *msym == 0 ) return TRUE;
if( *msym != '_' ) return FALSE;
msym++;
if( (*msym < 'A') || (*msym > 'Z') ) return FALSE;
msym++;
while(*msym) {
if( (*msym < '0') || (*msym > '9') ) return FALSE;
msym++;
}
return TRUE;
}
int
InterpNameLookupFileC(name, valp)
char *name;
unsigned *valp;
{
int ans;
char *tailP, *dotP;
char *fileName;
char *filePat;
char *procName;
int len;
XR_Pointer moduleStart, moduleLim;
XR_ILSymEntry ilse;
int i, caseSensitive;
/* break out file name, file pattern, proc name */
if( (dotP = strchr(name, '.')) == NIL ) {
return(-1);
}
tailP = strrchr(name, '.');
len = strlen(name);
fileName = (char *)XR_malloc(len+1);
bcopy( name, fileName, (tailP-name) );
fileName[tailP-name] = 0;
filePat = (char *)XR_malloc(len+3); /* 3 = 1+strlen(".*") */
bcopy( name, filePat, (dotP-name) );
strcpy( filePat+(dotP-name), ".*" );
procName = (char *)XR_malloc(name+len+1-tailP);
strcpy( procName, tailP+1 );
/* find module matching file name */
for( i = 0; i <= 1; i++ ) {
caseSensitive = (i == 0);
ilse = XR_ILGetMatchingSymEntryByName(
NIL, filePat, caseSensitive, WANT_ALL_TYPES, IGNORE_NONE, 0 );
for(;;) {
if( ilse == NIL )
break;
if( ((ilse->ilse_type & ILSE_TYPE) == ILSE_MODULE)
&& MimosaFileNameMatch(fileName, ilse->ilse_name,
caseSensitive) )
break;
ilse = XR_ILGetMatchingSymEntryByName(
ilse, filePat, caseSensitive,
WANT_ALL_TYPES, IGNORE_NONE, 1 );
}
if( ilse != NIL ) break;
}
if( ilse != NIL ) {
moduleStart = (XR_Pointer)(ilse->ilse_value);
#ifdef XXX
XR_ConsoleMsg("Module %s at 0x%x\n ...", fileName, moduleStart);
#endif
/* get most recently defined sym with given value */
ilse = XR_ILGetMatchingSymEntryByValue(
NIL, moduleStart, WANT_ALL_TYPES, IGNORE_NONE, 0 );
/* search backward for symbol with length */
for(;;) {
if( ilse == NIL ) break;
#ifdef XXX
XR_ConsoleMsg(" module sym %s type %d val 0x%x len 0x%x...\n", ilse->ilse_name, ilse->ilse_type, ilse->ilse_value, ilse->ilse_size);
#endif
if( ilse->ilse_value != moduleStart ) { ilse = NIL; break; }
if( (ilse->ilse_size > 0)
&& ((ilse->ilse_type & ILSE_TYPE) == ILSE_MODULE) )
break;
ilse = XR_ILGetMatchingSymEntryByValue(
ilse, 0, WANT_ALL_TYPES, IGNORE_NONE, (-1) );
}
/* (ilse != NIL) => <ilse_value, ilse_size> define module */
}
/* find the named symbol */
if( ilse != NIL ) {
moduleStart = (XR_Pointer)(ilse->ilse_value);
moduleLim = XR_ComputeAddress(
moduleStart, ilse->ilse_size, XR_DONT_ROUND );
#ifdef XXX
XR_ConsoleMsg("Module %s start 0x%x lim 0x%x...\n",
ilse->ilse_name, moduleStart, moduleLim);
#endif
for(;;) {
ilse = XR_ILGetMatchingSymEntryByValue(
ilse, 0, WANT_ALL_TYPES, IGNORE_NONE, 1 );
if( ilse == NIL )
{ break; }
#ifdef XXX
XR_ConsoleMsg(" examining sym %s val 0x%x...\n",
ilse->ilse_name, ilse->ilse_value);
#endif
if( (((XR_Pointer)(ilse->ilse_value)) < moduleStart) )
{ /* cant happen */ continue; }
if( (((XR_Pointer)(ilse->ilse_value)) >= moduleLim) )
{ ilse = NIL; break; }
if( MimosaSymMatch(procName, ilse->ilse_name) )
{ break; }
}
} else {
ilse = NIL;
}
if( ilse == NIL ) {
return(-1);
}
(*valp) = ilse->ilse_value;
return 0;
}
int
InterpNameLookup(name, valp, how)
char *name;
unsigned *valp;
char *how;
{
int ans;
unsigned val;
if( name == NIL ) return (-1);
if( valp == NIL ) valp = &val;
if( (how != NIL) && (how[0] == 0) ) how = NIL;
if( (how == NIL) || (strcasecmp(how, "C") == 0) ) {
ans = InterpNameLookupC(name, valp);
if( ans >= 0 ) return ans;
}
if( (how == NIL) || (strcasecmp(how, "Cedar") == 0) ) {
ans = InterpNameLookupCedar(name, valp);
if( ans >= 0 ) return ans;
}
if( (how == NIL) || (strcasecmp(how, "CFile") == 0) ) {
ans = InterpNameLookupFileC(name, valp);
if( ans >= 0 ) return ans;
}
return (-1);
}
static void
ScanSymbolConst(idata, text, e)
InterpData idata;
char *text;
Exp e;
{
int ans;
unsigned v;
ans = InterpNameLookup(text, &v, NIL);
if( ans >= 0 ) {
e->exp_type = TYPE_ADDRESS;
e->exp_value = v;
}
}
static Exp
ScanConst(idata, text)
InterpData idata;
char *text;
{
Exp e;
char c;
char *b;
int n, len;
e = NewExp(idata, TYPE_ERR, 0);
c = text[0];
if( ((c >= '0') && (c <= '9')) || (c == '-') || (c == '+') ) /* number */ {
n = strtol(text, &b, 0);
if( *b == 0 ) {
e->exp_type = TYPE_WORD32;
e->exp_value = (unsigned)n;
}
} else if( c == '"' ) {
text += 1;
len = strlen(text);
b = (char *)XR_malloc(1+len);
strcpy(b, text);
if( (len > 0) && (b[len-1] == '"') ) b[len-1] = 0;
e->exp_type = TYPE_STRING;
e->exp_value = (unsigned)b;
return e;
} else if( c == '\'' ) {
e->exp_type = TYPE_CHAR;
e->exp_value = (unsigned)(text[1]);
} else {
ScanSymbolConst(idata, text, e);
}
return e;
}
static int
ScanArgs(idata, theArgs)
InterpData idata;
char *theArgs;
{
char *p;
char *thisArg;
char delim;
int k;
Exp e;
int ntoks;
char argbuf[1024];
strcpy(&(argbuf[0]), theArgs);
for( ntoks = 0; ntoks < MAX_TOKENS; ntoks++ ) {
idata->idata_tokens[ntoks].tok_kind = TOK_EOF;
idata->idata_tokens[ntoks].tok_exp = NIL;
}
CLEAR_INTERP_ERR(idata);
ntoks = 0;
p = &(argbuf[0]);
for(;;) {
for(;;) {
if( *p == 0 ) goto Out;
if( (*p != ' ') && (*p != '\n') ) break;
p += 1;
}
thisArg = p;
switch( *p ) {
case '"':
case '\'':
delim = *p;
break;
default:
delim = ' ';
break;
}
p += 1;
for(;;) {
if( *p == 0 ) break;
if( (*p == delim) || (*p == '\n') ) { *p++ = 0; break; }
p += 1;
}
if( (k = ReservedWordKind(thisArg)) != 0 ) {
e = NIL;
} else if( thisArg[0] == '&' ) {
k = TOK_VAR;
e = ScanVar(idata, thisArg);
if( e->exp_type == TYPE_ERR )
INTERP_ERR(idata, ntoks, "syntax error in variable");
} else {
k = TOK_CONST;
e = ScanConst(idata, thisArg);
if( e->exp_type == TYPE_ERR )
INTERP_ERR(idata, ntoks, "syntax error in constant");
if( e->exp_type == TYPE_UNDEFSYM )
INTERP_ERR(idata, ntoks, "undefined symbol");
}
if( ntoks < MAX_TOKENS ) {
idata->idata_tokens[ntoks].tok_kind = k;
idata->idata_tokens[ntoks].tok_exp = e;
ntoks += 1;
}
}
Out:
return ntoks;
}
static Tok
GetTok(idata, index)
InterpData idata;
int index;
{
return &(idata->idata_tokens[index]);
}
static bool
ExpMutable(exp)
Exp exp;
{
return (exp->exp_name != NIL);
}
static void
Assign(idata, eto, efrom)
InterpData idata;
Exp eto;
Exp efrom;
{
eto->exp_type = efrom->exp_type;
eto->exp_value = efrom->exp_value;
}
static Exp
CoerceToImmutable(idata, exp, resultType)
InterpData idata;
Exp exp;
int resultType;
{
Exp resultExp;
if( resultType == TYPE_NONE )
resultType = exp->exp_type;
if( ExpMutable(exp) || (resultType != exp->exp_type) ) {
resultExp = NewExp(idata, resultType, exp->exp_value);
} else {
resultExp = exp;
}
return resultExp;
}
static unsigned
CallFuncChild(self)
XR_MesaProc self;
{
CallRecord cr;
unsigned *a;
int ans;
cr = ((CallRecord)(self->mp_x));
XR_MonitorEntry( &(cr->cr_ml) );
/*
* It's now safe for InterpGetMyCR to read cr->cr_calleeThread
* and it will remain safe after lock is released ...
*/
XR_MonitorExit( &(cr->cr_ml) );
cr->cr_result = NIL;
a = cr->cr_args;
if( cr->cr_nArgs <= 6 ) {
ans = (*(cr->cr_calleeCProc))(a[0], a[1], a[2], a[3], a[4], a[5]);
} else /* if( cr->cr_nArgs <= 12 ) */ {
ans = (*(cr->cr_calleeCProc))(a[0], a[1], a[2], a[3], a[4], a[5],
a[6], a[7], a[8], a[9], a[10], a[11]);
}
XR_MonitorEntry( &(cr->cr_ml) );
if( cr->cr_idata != NIL )
cr->cr_result = NewExp( cr->cr_idata, TYPE_NONE, ((unsigned)(ans)) );
XR_Notify( &(cr->cr_cvDone) );
XR_MonitorExit( &(cr->cr_ml) );
return NIL;
}
static void
InsertOnCRList(cr)
CallRecord cr;
{
XR_MonitorEntry( &(callRecordsLock) );
cr->cr_next = callRecords;
callRecords = cr;
XR_MonitorExit( &(callRecordsLock) );
}
static void
DeleteFromCRList(cr)
CallRecord cr;
{
CallRecord p, prev;
if( cr == NIL ) return;
XR_MonitorEntry( &(callRecordsLock) );
p = callRecords; prev = NIL;
while( (p != cr) && (p != NIL) ) {
prev = p; p = p->cr_next;
}
if( p != NIL ) {
if( prev == NIL ) {
callRecords = p->cr_next;
} else {
prev->cr_next = p->cr_next;
}
cr->cr_next = NIL;
}
XR_MonitorExit( &(callRecordsLock) );
}
static Exp
CallFunc(idata, func, args, nArgs, doFork)
InterpData idata;
CProc func;
unsigned *args;
int nArgs;
bool doFork;
{
CallRecord cr;
int ans;
Exp result;
if( func == NIL ) return ((Exp)(-1));
if( nArgs > 12 ) {
MSG1( idata,
"Warning: args after 12th (of %d) probably ignored\n", nArgs);
}
cr = (CallRecord)(XR_calloc(1, sizeof(struct CallRecordRep)));
cr->cr_idata = idata;
cr->cr_calleeCProc = func;
cr->cr_calleeMProc.mp_proc = CallFuncChild;
cr->cr_calleeMProc.mp_x = ((unsigned)(cr));
cr->cr_nArgs = nArgs;
(void)bcopy(args, &(cr->cr_args[0]), nArgs*sizeof(unsigned));
InsertOnCRList(cr);
if( doFork ) {
XR_InitializeCondition( &(cr->cr_cvDone), XR_WAIT_FOREVER );
if( idata->idata_callTimeoutMsec != 0 ) {
XR_SetTimeout( &(cr->cr_cvDone),
XR_MsecToTicks(idata->idata_callTimeoutMsec) );
}
XR_EnableAborts( &(cr->cr_cvDone) );
XR_MonitorEntry(&(cr->cr_ml));
XR_Fork( &(cr->cr_calleeThread), &(cr->cr_calleeMProc) );
(void)XR_DetachCT( &(cr->cr_calleeThread) );
ans = XR_WaitCV( &(cr->cr_cvDone), &(cr->cr_ml) );
result = cr->cr_result;
cr->cr_idata = NIL;
XR_MonitorExit( &(cr->cr_ml) );
if( ans != 0 ) {
/* aborted ... */
result = ((Exp)(-1));
(void) XR_AbortCT( &(cr->cr_calleeThread) );
} else if( result == NIL ) {
/* timed out ... */
} else {
/* okay ... */
DeleteFromCRList(cr);
}
} else {
(void)((*(cr->cr_calleeMProc.mp_proc))(cr->cr_calleeMProc));
result = cr->cr_result;
cr->cr_idata = NIL;
DeleteFromCRList(cr);
}
return result;
}
int /* numTokensConsumed */
Interp(idata, startPos, resultPtr)
InterpData idata;
int startPos;
Exp *resultPtr;
{
int i, pos, ans;
Exp e1, e2, eRes;
unsigned callArgs[MAX_CALLARGS];
Tok theTok;
CLEAR_INTERP_ERR(idata);
pos = startPos;
theTok = GetTok(idata, pos);
switch( theTok->tok_kind ) {
case TOK_ASSIGN:
pos += 1;
if( (ans = Interp(idata, pos, &e1)) <= 0 )
INTERP_ERR(idata, pos, "bad assign lhs");
pos += ans;
if( ! ExpMutable(e1) )
INTERP_ERR(idata, pos, "assign lhs not var");
if( (ans = Interp(idata, pos, &eRes)) <= 0 )
INTERP_ERR(idata, pos, "bad assign rhs");
pos += ans;
eRes = CoerceToImmutable(idata, eRes, TYPE_NONE);
Assign(idata, e1, eRes);
break;
case TOK_LBRACKET2:
case TOK_LBRACKET:
pos += 1;
if( (ans = Interp(idata, pos, &e1)) <= 0 )
INTERP_ERR(idata, pos, "missing proc in call");
pos += ans;
e1 = CoerceToImmutable(idata, e1, TYPE_NONE);
(void)bzero(callArgs, (sizeof callArgs));
i = 0;
for(;;) {
if( GetTok(idata, pos)->tok_kind == TOK_RBRACKET ) {
pos += 1; break;
}
if( (ans = Interp(idata, pos, &e2)) <= 0 )
INTERP_ERR(idata, pos, "bad arg");
pos += ans;
if( i >= MAX_CALLARGS )
INTERP_ERR(idata, pos, "too many args in function call");
callArgs[i] =
CoerceToImmutable(idata, e2, TYPE_NONE)->exp_value;
i += 1;
}
eRes = CallFunc(idata, ((CProc)(e1->exp_value)), callArgs, i,
/*doFork:*/ (theTok->tok_kind == TOK_LBRACKET) );
if( eRes == NIL ) {
INTERP_ERR(idata, pos, "function call timed out");
} else if( eRes == ((Exp)(-1)) ) {
INTERP_ERR(idata, pos, "function call aborted ");
}
break;
case TOK_CAST:
pos += 1;
if( (ans = Interp(idata, pos, &e1)) <= 0 )
INTERP_ERR(idata, pos, "bad cast type exp");
pos += ans;
if( e1->exp_type == TYPE_NONE )
INTERP_ERR(idata, pos, "no type for cast type exp");
if( (ans = Interp(idata, pos, &e2)) <= 0 )
INTERP_ERR(idata, pos, "bad cast exp");
pos += ans;
eRes = CoerceToImmutable(idata, e2, e1->exp_type);
break;
case TOK_ADD:
case TOK_SUB:
case TOK_MUL:
case TOK_DIV:
pos += 1;
if( (ans = Interp(idata, pos, &e1)) <= 0 )
INTERP_ERR(idata, pos, "bad binary exp first operand");
pos += ans;
e1 = CoerceToImmutable(idata, e1, TYPE_WORD32);
if( (ans = Interp(idata, pos, &e2)) <= 0 )
INTERP_ERR(idata, pos, "bad cast exp");
pos += ans;
e2 = CoerceToImmutable(idata, e2, TYPE_WORD32);
switch( theTok->tok_kind ) {
case TOK_ADD: ans = e1->exp_value + e2->exp_value; break;
case TOK_SUB: ans = e1->exp_value - e2->exp_value; break;
case TOK_MUL: ans = e1->exp_value * e2->exp_value; break;
case TOK_DIV: ans = e1->exp_value / e2->exp_value; break;
}
eRes = NewExp( idata, TYPE_WORD32, ((unsigned)(ans)) );
break;
case TOK_DEREF:
pos += 1;
if( (ans = Interp(idata, pos, &e1)) <= 0 )
INTERP_ERR(idata, pos, "bad deref exp operand");
pos += ans;
e1 = CoerceToImmutable(idata, e1, TYPE_ADDRESS);
ans = e1->exp_value;
if( (ans < (64*1024)) || ((ans & 03) != 0) )
INTERP_ERR(idata, pos, "deref memory fault");
eRes = NewExp( idata, TYPE_WORD32, *((unsigned *)(ans)) );
break;
case TOK_VAR:
case TOK_CONST:
eRes = GetTok(idata, pos)->tok_exp;
pos += 1;
break;
default:
INTERP_ERR(idata, pos, "syntax error");
}
*resultPtr = eRes;
return (pos - startPos);
}
static void
PrintExp(idata, e)
InterpData idata;
Exp e;
{
char *s;
int i;
if( e == NIL ) {
MSG0(idata, "(nil)\n");
return;
}
switch( e->exp_type ) {
case TYPE_ERR:
MSG0(idata, "(error)\n");
break;
case TYPE_CHAR:
MSG2(idata, "'%c' (0x%x)\n",
e->exp_value, e->exp_value);
break;
case TYPE_STRING:
s = (char *)(e->exp_value);
MSG0(idata, "\"");
for( i = 0; i < 250; i++ ) {
if( (*s) == 0 ) break;
MSG1(idata, "%c", *s);
s++;
}
MSG0(idata, "\"\n");
break;
case TYPE_ADDRESS:
MSG1(idata, "0x%x -> ", e->exp_value);
if( ValidAddress(e->exp_value, 1, sizeof(unsigned)) ) {
MSG2(idata, "%d (0x%x)\n",
*((unsigned *)(e->exp_value)),
*((unsigned *)(e->exp_value)) );
} else {
MSG0(idata, "(bad address)\n");
}
break;
default:
MSG2(idata, "%d (0x%x)\n",
e->exp_value, e->exp_value);
break;
}
}
/*
* Stuff that can be used by functions called from the interpreter
*/
static CallRecord
InterpGetMyCR()
{
struct XR_CTRep me;
CallRecord ans = NIL;
CallRecord cr;
XR_GetCurrent( &me );
XR_MonitorEntry( &(callRecordsLock) );
for( cr = callRecords; cr != NIL; cr = cr->cr_next ) {
if( (cr->cr_calleeThread.ct_thread == me.ct_thread)
&& (cr->cr_calleeThread.ct_gen == me.ct_gen) ) {
ans = cr;
break;
}
}
XR_MonitorExit( &(callRecordsLock) );
return ans;
}
int
InterpCallNArgs()
{
CallRecord cr;
cr = InterpGetMyCR();
if( cr == NIL ) return (-1);
return cr->cr_nArgs;
}
unsigned *
InterpCallArgs()
{
CallRecord cr;
cr = InterpGetMyCR();
if( cr == NIL ) return NIL;
return cr->cr_args;
}
void
InterpCallMsg4(fmt, x1, x2, x3, x4)
char *fmt;
unsigned x1, x2, x3, x4;
{
CallRecord cr;
cr = InterpGetMyCR();
if( cr == NIL ) {
MSG4(NIL, fmt, x1, x2, x3, x4);
} else {
XR_MonitorEntry( &(cr->cr_ml) );
MSG4(cr->cr_idata, fmt, x1, x2, x3, x4);
XR_MonitorExit( &(cr->cr_ml) );
}
}
/*
* Primitives -- stuff it's useful to call from the interpreter
*/
int
wpoke()
/*
eval [ wpoke addr word1 ... wordk ]
store word1 ... wordk in conscutive locations starting at addr
*/
{
CallRecord cr;
unsigned *pFrom;
int n;
unsigned *pTo;
if( (cr = InterpGetMyCR()) == NIL ) return (-1);
pFrom = cr->cr_args;
n = cr->cr_nArgs;
if( pFrom == NIL ) return (-2);
pTo = (unsigned *)(*pFrom++); n -= 1;
if( pTo == NIL ) return (-3);
if( !ValidAddress( ((XR_Pointer)(pTo)), n, sizeof(unsigned) ) ) {
InterpCallMsg4("(bad address 0x%x)\n", pTo, 0, 0, 0);
return (-4);
}
while( n > 0 ) { *pTo++ = *pFrom++; n -= 1; }
return 0;
}
int
bpoke()
/*
like wpoke but byte-by-byte
*/
{
CallRecord cr;
unsigned *pFrom;
int n;
unsigned char *pTo;
if( (cr = InterpGetMyCR()) == NIL ) return (-1);
pFrom = cr->cr_args;
n = cr->cr_nArgs;
if( pFrom == NIL ) return (-2);
pTo = (unsigned char *)(*pFrom++); n -= 1;
if( pTo == NIL ) return (-3);
if( !ValidAddress( ((XR_Pointer)(pTo)), n, sizeof(char) ) ) {
InterpCallMsg4("(bad address 0x%x)\n", pTo, 0, 0, 0);
return (-4);
}
while( n > 0 ) { *pTo++ = *pFrom++; n -= 1; }
return 0;
}
int
wpeek()
/*
eval [ wpeek addr nwords ]
print contents of nwords memory locations starting at addr
*/
{
CallRecord cr;
unsigned *ap;
int acnt;
unsigned n, x;
unsigned *p;
if( (cr = InterpGetMyCR()) == NIL ) return (-1);
ap = cr->cr_args;
acnt = cr->cr_nArgs;
if( acnt <= 0 ) return (-2);
if( ap == NIL ) return (-3);
p = ((unsigned *)(*ap++)); acnt--;
n = ((acnt > 0) ? (*ap++) : 1 );
if( n == 0 ) return 0;
if( !ValidAddress( ((XR_Pointer)(p)), n, sizeof(unsigned) ) ) {
InterpCallMsg4("(bad address 0x%x)\n", p, 0, 0, 0);
return 0;
}
while( n > 0 ) {
x = *p++; n--;
InterpCallMsg4("%d (0x%x)%s", x, x, ((n > 0) ? (", ") : ("\n")), 0);
}
return ((int)(x));
}
int
bpeek()
/*
like wpeek but byte-by-byte
*/
{
CallRecord cr;
unsigned *ap;
int acnt;
unsigned n, x;
unsigned *p;
unsigned char *pFrom;
if( (cr = InterpGetMyCR()) == NIL ) return (-1);
ap = cr->cr_args;
acnt = cr->cr_nArgs;
if( acnt <= 0 ) return (-2);
if( ap == NIL ) return (-3);
p = ((unsigned *)(*ap++)); acnt--;
n = ((acnt > 0) ? (*ap++) : 1 );
if( n == 0 ) return 0;
if( !ValidAddress( ((XR_Pointer)(p)), n, sizeof(unsigned char) ) ) {
InterpCallMsg4("(bad address 0x%x)\n", p, 0, 0, 0);
return 0;
}
pFrom = ((unsigned char *)(p));
while( n > 0 ) {
x = *pFrom++; n--;
InterpCallMsg4("%d (0x%x)%s", x, x, ((n > 0) ? (", ") : ("\n")), 0);
}
return ((int)(x));
}
int
wnew()
/*
eval [ wnew word1 ... wordk ]
allocate a collectable object filled with words word1 ... wordk
*/
{
CallRecord cr;
unsigned *ap;
int len;
unsigned *p;
if( (cr = InterpGetMyCR()) == NIL ) return (-1);
ap = cr->cr_args;
len = cr->cr_nArgs * sizeof(unsigned);
if( len <= 0 ) return (-2);
if( ap == NIL ) return (-3);
p = (unsigned *)(XR_malloc( len ));
(void)bcopy( ((char *)(ap)), ((char *)(p)), len );
return ((int)(p));
}
/*
* The body of the interpreter ...
*/
static int
DoInterpUnderLock(idata, cmd)
InterpData idata;
char *cmd;
{
Exp e, e2;
int pos, ans;
bool gotValue = FALSE;
while( (*cmd != 0) && (*cmd != ' ') ) cmd++;
ans = ScanArgs(idata, cmd);
if( ans < 0 ) {
MSG2(idata, "ERROR: %s (at token %d)\n",
idata->idata_errMsg, idata->idata_errPos );
return 0;
}
pos = 0;
while( GetTok(idata, pos)->tok_kind != TOK_EOF ) {
if( GetTok(idata, pos)->tok_kind == TOK_SEPARATOR ) {
ans = 1;
} else {
ans = Interp(idata, pos, &e);
gotValue = TRUE;
}
if( ans <= 0 ) {
MSG2(idata, "ERROR: %s (at token %d)\n",
idata->idata_errMsg, idata->idata_errPos );
return 0;
}
pos += ans;
}
if( gotValue ) {
idata->idata_varIndex += 1;
e2 = ScanVar(idata, "&");
Assign(idata, e2, e);
MSG1(idata, "&%d: ", idata->idata_varIndex );
PrintExp(idata, e);
} else {
MSG0(idata, "\n");
}
return 0;
}
static unsigned
DesperationPutProc(s, d)
char *s;
XR_Pointer d;
{
XR_ConsoleMsg("%s", s);
return 0;
}
/*
* exported for use by Mesa, etc ...
*/
int
XR_Interp(idata, putProc, putClientData, callTimeoutMsec, cmd)
InterpData idata;
CProc putProc /*(char *s, XR_Pointer clientData)*/;
XR_Pointer putClientData;
unsigned callTimeoutMsec;
char *cmd;
{
int ans;
CProc savPutProc;
XR_Pointer savPutClientData;
unsigned savCallTimeoutMSec;
if( idata == NIL ) idata = interpData;
XR_MonitorEntry( &(idata->idata_ml) );
savPutProc = idata->idata_putProc;
savPutClientData = idata->idata_putClientData;
savCallTimeoutMSec = idata->idata_callTimeoutMsec;
if( putClientData != NIL ) {
if( putProc == NIL ) putProc = *((CProc *)(putClientData));
}
if( putProc != NIL ) {
idata->idata_putProc = putProc;
idata->idata_putClientData = putClientData;
} else if( savPutProc == NIL ) {
idata->idata_putProc = DesperationPutProc;
idata->idata_putClientData = NIL;
}
idata->idata_callTimeoutMsec = callTimeoutMsec;
ans = DoInterpUnderLock(idata, cmd);
idata->idata_putProc = savPutProc;
idata->idata_putClientData = savPutClientData;
idata->idata_callTimeoutMsec = savCallTimeoutMSec;
XR_MonitorExit( &(idata->idata_ml) );
return ans;
}
InterpData
XR_MakeInterpHandle()
{
InterpData idata =
((InterpData)(XR_calloc( 1, sizeof(struct InterpDataRep))));
return idata;
}
void
XR_SetInterpPutProc(idata, proc, data)
InterpData idata;
CProc proc;
XR_Pointer data;
{
if( idata == NIL ) idata = interpData;
XR_MonitorEntry( &(idata->idata_ml) );
idata->idata_putProc = proc;
idata->idata_putClientData = data;
XR_MonitorExit( &(idata->idata_ml) );
}
/*
* Stuff for registration with PCR command loop
*/
static int
InterpPutProc(s, clce)
char *s;
XR_CLCallEnv clce;
{
if( s != NIL )
(*(clce->clce_msgSink->mp_proc))(s, strlen(s), clce->clce_msgSink);
return 0;
}
static char *
ReconstructCmdLine(argc, argv)
int argc;
char **argv;
{
int i, len, totalLen;
char *result;
totalLen = 1; /* for trailing null if argc == 0 */
for( i = 0; i < argc; i++ ) totalLen += (1+strlen(argv[i]));
result = (char *)XR_malloc(totalLen);
totalLen = 0;
i = 0;
for(;;) {
len = strlen(argv[i]);
(void)bcopy(argv[i], &(result[totalLen]), len);
totalLen += len;
if( (++i) >= argc ) break;
result[totalLen] = ' ';
totalLen += 1;
}
result[totalLen] = 0;
return result;
}
static /* XR_CLProc */ int
InterpCLProc(clce, argc, argv, prevResult, self)
XR_CLCallEnv clce;
int argc;
char **argv;
int prevResult;
XR_MesaProc self;
{
(void) XR_Interp(
/* interpdata */ self->mp_x,
InterpPutProc, clce,
defaultCallTimeoutMsec,
ReconstructCmdLine(argc, argv)
);
return 1;
}
int
XR_run_Interp() {
interpData = XR_MakeInterpHandle();
interpDataForConstEval = XR_MakeInterpHandle();
(void)XR_CLRegisterProc(
XR_globalCLProcsHandle,
"eval", FALSE, "evaluate a simple expression",
XR_MakeMesaProc(InterpCLProc, interpData),
TRUE
);
}
/*
* Program debugging interface from C -- print console messages, save values to
* be examined with interpreter.
*/
static int XR_doSaveForInterp = 2;
/* 0 => disabled, 1 => save but don't print, 2 => save and print */
int XR_SetSaveForInterp(x) int x; { XR_doSaveForInterp = x; return x; }
int XR_GetSaveForInterp() { return XR_doSaveForInterp; }
int
XR_SaveForCInterp(p, m)
XR_Pointer p; /* value to be saved */
char *m; /* optional message to print */
{
InterpData idata = interpDataForConstEval;
int varIndex = 0;
Exp lhs, rhs;
XR_MonitorEntry( &(idata->idata_ml) );
if( (p != NIL) && (XR_doSaveForInterp > 0) ) {
varIndex = (idata->idata_varIndex += 1);
lhs = ScanVar(idata, "&");
rhs = NewExp(idata, TYPE_NONE, p);
Assign(idata, lhs, rhs);
}
if( (m != NIL) && (XR_doSaveForInterp > 1) ) {
MSG1( idata, "0x%x ", p);
if( varIndex != 0 )
MSG1( idata, "(saved as &%d) ", varIndex );
MSG1( idata, "%s\n", m );
}
XR_MonitorExit( &(idata->idata_ml) );
return varIndex;
}
/*
* Interface from Cedar ...
*/
/* rope<->string */
static char * (*uxStringsDotCreate)() = NIL;
char *
XR_CharStarFromRope(r, r2)
XR_Pointer r, r2; /* r2 for backward compatibility, should go away */
{
if( r == NIL ) r = r2;
if( !ValidAddress(r, 1, 4) ) r = NIL;
if( uxStringsDotCreate == NIL ) {
Exp e;
XR_MonitorEntry( &(interpDataForConstEval->idata_ml) );
e = ScanConst(interpDataForConstEval, "UXStringsImpl.Create");
if( e != NIL )
*((unsigned *)(&uxStringsDotCreate)) = e->exp_value;
XR_MonitorExit( &(interpDataForConstEval->idata_ml) );
if( uxStringsDotCreate == NIL ) return NIL;
}
return (*uxStringsDotCreate)(r, NIL);
}
static XR_Pointer (*uxStringsDotToRope)(/*r, len*/) = NIL;
XR_Pointer
XR_RopeFromCharStar(s, s2)
char *s, *s2;
{
if( s == NIL ) s = s2;
if( s == NIL ) s = "(nil)";
else if( !ValidAddress(s, 1, 1) ) s = "(bad address)";
if( uxStringsDotToRope == NIL ) {
Exp e;
XR_MonitorEntry( &(interpDataForConstEval->idata_ml) );
e = ScanConst(interpDataForConstEval, "UXStringsImpl.ToRope");
if( e != NIL )
*((unsigned *)(&uxStringsDotToRope)) = e->exp_value;
XR_MonitorExit( &(interpDataForConstEval->idata_ml) );
if( uxStringsDotToRope == NIL ) return NIL;
}
return (*uxStringsDotToRope)(s, strlen(s)+16 );
}
/* console messages */
void
XR_ConsoleMsgRope(r)
XR_Pointer r;
{
XR_ConsoleMsg("%s", XR_CharStarFromRope(r));
}
int
XR_SaveForMesaInterp(p, m)
XR_Pointer p; /* value to be saved */
XR_Pointer m; /* optional message to print */
{
return XR_SaveForCInterp(p, XR_CharStarFromRope(NIL, m) );
}
/* the following is for historic reasons ... */
int
XR_SaveForInterp(p, m)
XR_Pointer p; /* value to be saved */
XR_Pointer m; /* optional message to print */
{
return XR_SaveForMesaInterp(p, m);
}